package org.melanesia.test;

import org.melanesia.sql.PaginationSupport;

public class PostgresPaginationSupport extends PaginationSupport {

    private static final String QUERY_VARIABLE = "!QUERY!";

    private static final String RN_VARIABLE = "!RN!";
    private static final String RN_START_VARIABLE = "!RN_START!";
    private static final String RN_FETCH_VARIABLE = "!RN_FETCH!";

    private static final int RN_INDEX = 0;
    private static final int RN_START_INDEX = 1;
    private static final int RN_FETCH_INDEX = 2;

    private static final String COUNT_QUERY_TEMPLATE = "SELECT COUNT(1) FROM ( !QUERY! )";
    private static final String RANGE_QUERY_TEMPLATE = "SELECT T.* FROM (!QUERY!) T OFFSET :!RN_START! LIMIT :!RN_FETCH!";
    private static final String TOP_ROWS_QUERY_TEMPLATE = "SELECT T.* FROM (!QUERY!) T LIMIT :!RN_FETCH!";
    private static final String BOTTOM_ROWS_QUERY_TEMPLATE = "SELECT * FROM (!QUERY!) T OFFSET :!RN_START!";

    @Override
    public void prepare() {

        String[] parameterNames = generateUniqueParameterNames("RANGE_", 3);

        setSql(SQLVariation.COUNT_QUERY, wrapOriginalQuery(COUNT_QUERY_TEMPLATE));

        switch (getBoundaryType()) {
        case RANGE:

            setSql(SQLVariation.RANGED_QUERY, wrapOriginalQuery(RANGE_QUERY_TEMPLATE, parameterNames));

            setParameter(SQLVariation.RANGED_QUERY, parameterNames[RN_START_INDEX], getStartRow());
            setParameter(SQLVariation.RANGED_QUERY, parameterNames[RN_FETCH_INDEX], getStopRow() - getStartRow() + 1);

            ignoreColumn(SQLVariation.RANGED_QUERY, parameterNames[RN_INDEX]);
            break;

        case TOP_ROWS:

            setSql(SQLVariation.RANGED_QUERY, wrapOriginalQuery(TOP_ROWS_QUERY_TEMPLATE, parameterNames));

            setParameter(SQLVariation.RANGED_QUERY, parameterNames[RN_FETCH_INDEX], getStopRow() - getStartRow() + 1);

            break;

        case BOTTOM_ROWS:

            setSql(SQLVariation.RANGED_QUERY, wrapOriginalQuery(BOTTOM_ROWS_QUERY_TEMPLATE, parameterNames));

            setParameter(SQLVariation.RANGED_QUERY, parameterNames[RN_START_INDEX], getStartRow());

            ignoreColumn(SQLVariation.RANGED_QUERY, parameterNames[RN_INDEX]);
            break;

        }

    }

    private String wrapOriginalQuery(final String template) {
        return wrapOriginalQuery(template, null);
    }

    private String wrapOriginalQuery(final String template, final String[] parameterNames) {
        String wrapped = template
            .replace(QUERY_VARIABLE, getOriginalSql());

        if (parameterNames != null && parameterNames.length > 0) {

            wrapped = wrapped
                    .replace(RN_VARIABLE, parameterNames[RN_INDEX])
                    .replace(RN_START_VARIABLE, parameterNames[RN_START_INDEX])
                    .replace(RN_FETCH_VARIABLE, parameterNames[RN_FETCH_INDEX]);

        }

        return wrapped;
    }
}
